"""
统一的参数高效微调(PEFT)配置工厂。

此模块为所有PEFT方法提供集中的配置创建系统，确保不同实现之间的配置处理一致性。
包含：
1. 基于方法名称创建配置
2. 基于骨干网络自动配置
3. 配置验证和合并
4. 类型安全的配置访问

设计原则：
1. 保持类型安全：所有函数都有完整的类型注解
2. 灵活的输入：支持从字典或已有配置对象创建
3. 统一错误处理：使用日志和明确的异常信息
4. 符合最小惊讶原则：命名和行为保持一致性
"""

from typing import Dict, Any, Union, Optional, Type, TypeVar, cast, overload, Literal
from dataclasses import is_dataclass, asdict, fields

from .config import (
    BasePEFTConfig,
    LoRAConfig,
    BypassLoRAConfig,
    PromptConfig,
    L2pConfig,
    CONFIG_REGISTRY,
)
from continuallearning.utils.logging import get_logger

logger = get_logger(__name__)

# 类型变量，用于泛型函数
T = TypeVar("T", bound=BasePEFTConfig)

# ======================= 字典参数的重载 =======================


@overload
def create_peft_config(
    method: Literal["lora"], config: Dict[str, Any]
) -> LoRAConfig: ...


@overload
def create_peft_config(
    method: Literal["bypass_lora"], config: Dict[str, Any]
) -> BypassLoRAConfig: ...


@overload
def create_peft_config(
    method: Literal["prompt"], config: Dict[str, Any]
) -> PromptConfig: ...


@overload
def create_peft_config(method: Literal["l2p"], config: Dict[str, Any]) -> L2pConfig: ...


@overload
def create_peft_config(method: str, config: Dict[str, Any]) -> BasePEFTConfig: ...


# ======================= 配置对象参数的重载 =======================


@overload
def create_peft_config(
    method: Literal["lora"], config: BasePEFTConfig
) -> LoRAConfig: ...


@overload
def create_peft_config(
    method: Literal["bypass_lora"], config: BasePEFTConfig
) -> BypassLoRAConfig: ...


@overload
def create_peft_config(
    method: Literal["prompt"], config: BasePEFTConfig
) -> PromptConfig: ...


@overload
def create_peft_config(method: Literal["l2p"], config: BasePEFTConfig) -> L2pConfig: ...


@overload
def create_peft_config(method: str, config: BasePEFTConfig) -> BasePEFTConfig: ...


# ======================= 无参数的重载 =======================


@overload
def create_peft_config(method: Literal["lora"]) -> LoRAConfig: ...


@overload
def create_peft_config(method: Literal["bypass_lora"]) -> BypassLoRAConfig: ...


@overload
def create_peft_config(method: Literal["prompt"]) -> PromptConfig: ...


@overload
def create_peft_config(method: Literal["l2p"]) -> L2pConfig: ...


@overload
def create_peft_config(method: str) -> BasePEFTConfig: ...


def create_peft_config(
    method: str, config: Optional[Union[Dict[str, Any], BasePEFTConfig]] = None
) -> BasePEFTConfig:
    """
    创建指定PEFT方法的配置对象。

    此工厂函数提供了统一的方式来创建不同PEFT方法的配置对象，
    处理各种输入类型并确保类型安全。

    Args:
        method: PEFT方法名称（例如"lora"、"prefix_tuning"、"prompt"）
        config: 可选的配置字典或对象，用于初始化

    Returns:
        BasePEFTConfig: 特定于请求方法的配置对象

    Raises:
        ValueError: 如果方法不受支持或配置无效
    """
    # 标准化方法名称
    method_lower = method.lower()

    # 检查方法是否受支持
    if method_lower not in CONFIG_REGISTRY:
        supported = ", ".join(CONFIG_REGISTRY.keys())
        raise ValueError(f"不支持的PEFT方法: '{method}'. 支持的方法有: {supported}")

    # 获取对应的配置类
    config_class = CONFIG_REGISTRY[method_lower]

    # 如果只提供了方法名称，创建默认配置
    if config is None:
        logger.debug(f"创建默认{method}配置")
        # 创建默认配置，并确保peft_type字段正确
        instance = config_class()
        if hasattr(instance, "peft_type"):
            instance.peft_type = method_lower
        return instance

    # 处理已有的配置对象
    if is_dataclass(config):
        logger.debug(f"从现有配置对象创建{method}配置")
        # 将配置对象转换为字典
        config_dict = asdict(config)
        # 确保peft_type与请求的方法匹配
        if "peft_type" in config_dict:
            config_dict["peft_type"] = method_lower

        # 移除不应该用于初始化的字段
        # 获取配置类的所有字段
        config_fields = fields(config_class)
        # 找出所有 init=False 的字段
        non_init_fields = [f.name for f in config_fields if not f.init]
        # 从字典中移除这些字段
        for field_name in non_init_fields:
            config_dict.pop(field_name, None)

        # 创建新的配置实例
        try:
            return config_class(**config_dict)
        except TypeError as e:
            # 记录详细错误信息
            logger.error(f"无法从配置对象创建{method}配置: {e}")
            logger.error(f"提供的配置: {config_dict}")
            logger.error(f"预期的字段: {[f.name for f in fields(config_class)]}")
            raise ValueError(f"配置无效: {str(e)}")

    # 处理配置字典
    if isinstance(config, dict):
        logger.debug(f"从字典创建{method}配置")
        # 创建副本以避免修改原始字典
        config_dict = dict(config)
        # 确保peft_type与请求的方法匹配
        if "peft_type" in config_dict:
            config_dict["peft_type"] = method_lower

        # 创建配置实例
        try:
            return config_class(**config_dict)
        except TypeError as e:
            # 记录详细错误信息
            logger.error(f"无法从字典创建{method}配置: {e}")
            logger.error(f"提供的配置: {config_dict}")
            logger.error(f"预期的字段: {[f.name for f in fields(config_class)]}")
            raise ValueError(f"配置无效: {str(e)}")

    # 无效的配置类型
    raise TypeError(f"配置必须是字典或数据类实例，得到的是{type(config)}")


# 为需要明确类型的场景提供便捷辅助函数
T = TypeVar("T", bound=BasePEFTConfig)


def create_peft_config_typed(
    config_type: str, config_dict: Dict[str, Any], expected_type: Type[T]
) -> T:
    """
    Create a PEFT configuration with explicit type checking.

    Args:
        config_type: Type of PEFT configuration to create
        config_dict: Dictionary with configuration parameters
        expected_type: The expected configuration type

    Returns:
        T: Configuration instance with the specified type

    Raises:
        TypeError: If the created config is not of the expected type
    """
    config = create_peft_config(config_type, config_dict)
    if not isinstance(config, expected_type):
        raise TypeError(
            f"Expected {expected_type.__name__} but got {type(config).__name__}"
        )
    return cast(T, config)


def config_to_dict(config: Union[Dict[str, Any], BasePEFTConfig]) -> Dict[str, Any]:
    """
    将配置对象转换为字典。

    Args:
        config: 配置对象或字典

    Returns:
        Dict[str, Any]: 配置的字典表示
    """
    if is_dataclass(config):
        return asdict(config)
    elif isinstance(config, dict):
        return config
    else:
        raise TypeError(f"Expected dataclass or dict, got {type(config)}")


def merge_configs(
    base_config: Union[Dict[str, Any], BasePEFTConfig],
    override_config: Union[Dict[str, Any], BasePEFTConfig],
) -> Dict[str, Any]:
    """
    合并两个配置，后者的值覆盖前者的值。

    Args:
        base_config: 基础配置（字典或对象）
        override_config: 覆盖配置（字典或对象）

    Returns:
        Dict[str, Any]: 合并后的配置字典
    """
    # 转换为字典
    base_dict = config_to_dict(base_config)
    override_dict = config_to_dict(override_config)

    # 合并字典
    merged = {**base_dict, **override_dict}

    return merged


def validate_config(
    config: Union[Dict[str, Any], BasePEFTConfig], config_type: Optional[str] = None
) -> bool:
    """
    验证配置是否有效。

    Args:
        config: 要验证的配置
        config_type: 可选的配置类型（如果已知）

    Returns:
        bool: 如果配置有效则返回True

    Raises:
        ValueError: 如果配置无效
    """
    # 如果是字典，尝试转换为对象来验证
    if isinstance(config, dict):
        # 确定配置类型
        if config_type is None:
            if "peft_type" in config:
                config_type = config["peft_type"]
            else:
                raise ValueError(
                    "无法确定配置类型，请提供config_type参数或在配置中包含peft_type"
                )

        # 创建配置对象来验证
        try:
            # 确保config_type不为None
            assert config_type is not None
            create_peft_config(config_type, config)
            return True
        except (ValueError, TypeError) as e:
            logger.error(f"配置验证失败: {e}")
            raise ValueError(f"配置验证失败: {e}")

    # 如果已经是配置对象，假定它已经通过了__post_init__验证
    return True


def get_field_metadata(config_type: str, field_name: str) -> Dict[str, Any]:
    """？看不懂，多点讲解
    获取配置字段的元数据。

    Args:
        config_type: 配置类型名称
        field_name: 字段名称

    Returns:
        Dict[str, Any]: 字段元数据

    Raises:
        ValueError: 如果配置类型或字段名称无效
    """
    # 创建默认配置来获取类
    config = create_peft_config(config_type, {})
    config_class = config.__class__

    # 查找字段
    for field in fields(config_class):
        if field.name == field_name:
            metadata = field.metadata if hasattr(field, "metadata") else {}
            # 确保返回的是Dict[str, Any]类型
            return dict(metadata)

    raise ValueError(f"字段'{field_name}'在'{config_type}'配置中不存在")
